using System;
using BeeDevelopment.Brazil;
using Stetson.Emulator.Hardware;

namespace Stetson.Emulator
{
	public partial class TI83Plus : Z80A
	{
		#region Devices

		private readonly IOPort linkPort;
		public IOPort LinkPort
		{ 
			get
			{ 
				return this.linkPort;
			}
		}

		private readonly T6A04 lcdDriver;
		public T6A04 LcdDriver
		{
			get
			{ 
				return this.lcdDriver;
			}
		}

		private readonly Keypad keypad;
		public Keypad Keypad
		{ 
			get
			{ 
				return this.keypad;
			}
		}

		#endregion

		#region I/O

        public byte GetLinkPortValue()
        {
            return this.linkPort.ReadStatus();
        }

        public void SetLinkPortValue(byte value)
        {
            this.linkPort.WriteExternalStatus(value);
        }

		public override byte ReadHardware(ushort port) 
		{
			switch (port & 0xFF) 
			{
				case 0x00:
					return this.linkPort.ReadStatus();
				case 0x01:
					return this.keypad.Read();
				case 0x02:
					return 0x3B;
				case 0x03: 
					return this.ReadInterruptMask();
				case 0x04:
					return this.ReadInterruptDeviceIndentification();
				case 0x06: 
					return this.CurrentPageA;
				case 0x07: 
					return this.CurrentPageB;
				case 0x10: 
					return this.lcdDriver.ReadControl();
				case 0x11: 
					return this.lcdDriver.ReadData();
	          case 0x14:
					return (byte)(this.FlashUnlocked ? 0x01 : 0x00);
				default:
					return 0xFF;
			}
		}

		public override void WriteHardware(ushort port, byte value) 
		{
			if ((port & 0xFF) == 0xFF) 
				Console.WriteLine((char)value);
			switch (port & 0xFF)
			{
				case 0x00: // Link
					this.linkPort.WriteStatus(value);
                    foreach (TI83Plus calc in LinkedCalculators)
                        calc.SetLinkPortValue(value);
					break;
				case 0x01: // Keypad
					this.keypad.Write(value); 
					break;
				case 0x03: // Interrupt mask
					this.WriteInterruptMask(value);
					break;
				case 0x04: // Memory map
					this.WriteMemoryMapControl(value);
					break;
				case 0x06: // Memory bank A
					this.WriteMemoryPageA(value);
					break;
				case 0x07: // Memory bank B
					this.WriteMemoryPageB(value); 
					break;
				case 0x10: // LCD control
					this.lcdDriver.WriteControl(value);
					break;
				case 0x11: // LCD data
					this.lcdDriver.WriteData(value); 
					break;
	          case 0x14: // Flash Control
					if (IsProtectedOutReady())
					   FlashUnlocked = value == 1;
					break;
				default:
					break;
			}
		}
		
		bool IsProtectedOutReady()
		{
		    byte bank = GetPageAtAddress(RegisterPC);
		    return (protectedPortWriteSequence == 4 &&
		        (GetPageAtAddress(RegisterPC) == 0x1C ||
		         GetPageAtAddress(RegisterPC) == 0x1D ||
		         GetPageAtAddress(RegisterPC) == 0x1F));
		}

		#endregion
	}
}
